package com.zhiwang.planet.download

import android.annotation.SuppressLint
import android.app.*
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.FileProvider
import android.util.Log
import android.view.View
import android.widget.RemoteViews
import com.zhiwang.planet.BuildConfig
import com.zhiwang.planet.R
import com.zhiwang.planet.event.AppDownLoadProgressEvent
import com.zhiwang.planet.util.installIntent
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.*
import okio.*
import org.greenrobot.eventbus.EventBus
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.*
import java.util.concurrent.TimeUnit


class BackUpDownLoadService : Service(), ProgressListener {

    private val TAG = "BackUpDownLoadService"

    private val taskStack = Stack<Task>()
    private lateinit var notification: Notification
    private lateinit var remoteViews: RemoteViews
    private var url: String? = null
    private var name: String? = null
    private var notificationId: Int = -1
    private var isRunning = false
    private var disposable: Disposable? = null
    private var downloadingTitle: String = "下载中..."
    private var downloadCompleteTitle: String = "下载完成"

    companion object {
        const val URL_KEY = "FILE_DownLoad_URL"
        const val NAME_KEY = "NAME_DownLoad_URL"
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent != null) {
            val downLoadUrl = intent.getStringExtra(URL_KEY)
            val name = intent.getStringExtra(NAME_KEY)
            var task = Task(downLoadUrl, name)
            if (downLoadUrl != null && taskStack.contains(task).not()) {
                addTask(task)
                if (!isRunning)
                    trigger()
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }

    private fun trigger() {
        if (taskStack.isEmpty()) return
        var task = taskStack.pop()
        if (task == null)
            return
        if (isRunning) {
            return
        }
        this.url = task.url
        this.name = task.name
        val client = OkHttpClient.Builder()
                .addNetworkInterceptor { chain ->
                    val response = chain.proceed(chain.request())
                    response
                            .newBuilder().body(ProgressResponseBody(response.body(), this@BackUpDownLoadService))
                            .build()
                }.build()
        val request = Request.Builder()
                .url(url)
                .build()
        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call?, e: IOException) {
                if (BuildConfig.DEBUG)
                    Log.i(TAG, "下载失败: ${e.message}")
            }

            @SuppressLint("LogNotTimber")
            override fun onResponse(call: Call?, response: Response?) {
                if (response != null) {
                    createFile()
                    createNotification()
                    saveFileToLocalStorage(response)
                }
            }

        })

    }

    private fun createFile() {
        val fileName = nameFromUrl()
        file = File(Environment.getExternalStorageDirectory(), fileName)
        if (file.exists().not())
            file.createNewFile()
    }

    lateinit var file: File
    private fun saveFileToLocalStorage(response: Response) {
        var disposable = Observable.create<DownLoadInfo> {
            try {
                val bufFos = BufferedOutputStream(FileOutputStream(file))
                val contentLen = response.body()!!.contentLength()
                var byteRead = 0L
                val downLoadInfo = DownLoadInfo(contentLen, byteRead, false)
                val stream = response.body()!!.byteStream()
                val buff = ByteArray(4096 * 2)
                var len = -1
                isRunning = true
                while (stream.read(buff).also { len = it } != -1) {
                    bufFos.write(buff, 0, len)
                    bufFos.flush()
                    byteRead += len
                    downLoadInfo.byteRead = byteRead
                    downLoadInfo.done = byteRead == -1L
                    it.onNext(downLoadInfo)
                }
                downLoadInfo.byteRead = -1
                downLoadInfo.done = true
                it.onNext(downLoadInfo)
                it.onComplete()
                stream.close()
                bufFos.close()
                isRunning = false
                trigger()
            } catch (e: Exception) {
                //下载过程中出了问题
                e.printStackTrace()
            }


        }
                //事件产生太快，通知栏更新来不及，过滤一部分
                .sample(500, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    onProgressUpdate(it.totalByte, it.byteRead, it.byteRead == -1L)
                }, {

                }, {
                    notifyComplete()
                })
    }

    private fun nameFromUrl(): String {
        var fileName: String
        if (url!!.endsWith(".apk"))
            fileName = url!!.substring(url!!.lastIndexOf('/'), url!!.lastIndexOf('.'))
        else fileName = url!!.substring(url!!.lastIndexOf('/'))
        if (fileName.length > 20)
            fileName = fileName.substring(0, 11)

        return fileName.plus(".apk")
    }


    private lateinit var notificationManagerCompat: NotificationManagerCompat


    private fun createNotification() {
        notificationManagerCompat = NotificationManagerCompat.from(this)
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val pendingIntent = file.installIntent(this)
        if (Build.VERSION.SDK_INT >= 26) {
            val notificationChannel = NotificationChannel(BuildConfig.APPLICATION_ID, "DownLoadUi", NotificationManager.IMPORTANCE_LOW)
            notificationChannel.enableLights(true)
            notificationChannel.setShowBadge(false)
//            notificationChannel.setSound(null,null)
            notificationManager.createNotificationChannel(notificationChannel)
            remoteViews = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.download_notification_layout)
            notification = NotificationCompat
                    .Builder(this, BuildConfig.APPLICATION_ID)
                    .setTicker("趣味星球")
                    .setSmallIcon(R.mipmap.ic_download)
                    .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_download))
                    .setContent(remoteViews)
//                    .setContentIntent(pendingIntent)
                    .build()
            remoteViews.setTextViewText(R.id.download_title, this.name + " 下载中...")
            remoteViews.setTextViewText(R.id.download_complete_layout, "${this.name} 下载完成点击安装...")
//            notification.contentIntent = pendingIntent
            notificationId += 1
            notificationManagerCompat.notify(notificationId, notification)
        } else {

            remoteViews = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.download_notification_layout)
            notification = NotificationCompat
                    .Builder(this, BuildConfig.APPLICATION_ID)
                    .setTicker("趣味星球")
                    .setSmallIcon(R.mipmap.ic_download)
                    .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_download))
                    .setContent(remoteViews)
                    .build()
//            notification.contentIntent = pendingIntent
            notificationId += 1
            remoteViews.setTextViewText(R.id.download_title, this.name + " 下载中...")
            remoteViews.setTextViewText(R.id.download_complete_layout, "${this.name} 下载完成点击安装...")
            notificationManagerCompat.notify(notificationId, notification)
        }
    }

    private fun createApkInstallIntent(): PendingIntent {
        val intent = Intent()
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            intent.action = Intent.ACTION_INSTALL_PACKAGE
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive")
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        } else {
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
            intent.setDataAndType(FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, file),
                    "application/vnd.android.package-archive")
        }
        intent.`package` = "com.android.packageinstaller"
        return PendingIntent.getActivity(
                this,
                1,
                intent,
                FLAG_UPDATE_CURRENT)

    }

    override fun onProgressUpdate(totalBytes: Long, bytesRead: Long, done: Boolean) {
        if (done) {
            isRunning = false
        }
        notifyNotification(totalBytes, bytesRead, done)
        EventBus.getDefault().post(
                AppDownLoadProgressEvent((bytesRead * 100 / totalBytes).toString(), name
                        ?: ""))
    }

    private fun notifyComplete() {
        remoteViews.setViewVisibility(R.id.downloading_state_layout, View.GONE)
        remoteViews.setViewVisibility(R.id.download_complete_layout, View.VISIBLE)
        notification.contentIntent = PendingIntent.getActivity(
                this,
                1,
                file.installIntent(this),
                FLAG_UPDATE_CURRENT)
        notificationManagerCompat.notify(notificationId, notification)
        val progressEvent = AppDownLoadProgressEvent("100", name ?: "")
        progressEvent.apkFile = file
        EventBus.getDefault().post(progressEvent)
        startActivity(file.installIntent(this))
        trigger()
    }

    private fun notifyNotification(totalBytes: Long, bytesRead: Long, done: Boolean) {
        val progress = (bytesRead * 100f / totalBytes).toInt()
        remoteViews.setProgressBar(R.id.progressBar, 100, progress, false)
        if (isRunning) {
            notificationManagerCompat.notify(notificationId, notification)
        }

    }

    override fun onDestroy() {
        super.onDestroy()
        if (disposable != null && disposable!!.isDisposed.not()) {
            disposable!!.dispose()
        }
    }

    private fun addTask(task: Task) = taskStack.add(task)

    class ProgressResponseBody(var responseBody: ResponseBody?, var listener: ProgressListener) : ResponseBody() {

        var bufferSource: BufferedSource? = null

        override fun contentLength() = responseBody!!.contentLength()


        override fun contentType() = responseBody!!.contentType()


        override fun source(): BufferedSource {
            if (bufferSource == null) {
                bufferSource = source(responseBody!!.source()).buffer()
            }
            return bufferSource!!
        }

        private fun source(source: Source): Source {
            return object : ForwardingSource(source) {
                var totalBytesRead = 0L
                override fun read(sink: Buffer, byteCount: Long): Long {

                    val bytesRead = super.read(sink, byteCount)
                    // read() returns the number of bytes read, or -1 if this pagedList is exhausted.
                    totalBytesRead += if (bytesRead != -1L) bytesRead else 0
//                    listener.onProgressUpdate(responseBody!!.contentLength(), totalBytesRead, bytesRead == -1L)
                    return bytesRead
                }
            }
        }

    }

    class Task(var url: String, var name: String = "planet_game.apk") {


        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (javaClass != other?.javaClass) return false

            other as Task

            if (url != other.url) return false
            if (name != other.name) return false

            return true
        }

        override fun hashCode(): Int {
            var result = url.hashCode()
            result = 31 * result + name.hashCode()
            return result
        }
    }
}